package com.linshengjian.snowball.utils;

import com.linshengjian.snowball.annotation.Handler;
import com.linshengjian.snowball.annotation.translate.BasicConvertHandler;
import com.linshengjian.snowball.annotation.translate.IConvertHandler;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ObjectConvertUtils {

    private volatile static ObjectConvertUtils instance;

    public static ObjectConvertUtils getInstance(){
        if(instance==null){
            synchronized (ObjectConvertUtils.class){
                if(instance==null){
                    instance=new ObjectConvertUtils();
                }
            }
        }
        return instance;
    }

    /**
     * 获取类的所有字段
     * @param clazz
     * @return
     */
    public List<Field> getFields(Class clazz) {
        List<Field> result = new ArrayList<>();
        do {
            result.addAll(Arrays.asList(clazz.getDeclaredFields()));
            clazz = clazz.getSuperclass();
        }while (clazz != null && !clazz.equals(Object.class));

        return result;
    }

    /**
     * 首字母大写
     * @param name
     * @return
     */
    private String captureName(String name) {
        char[] cs=name.toCharArray();
        if (cs[0] >= 97)
            cs[0]-=32;
        return String.valueOf(cs);

    }

    /**
     * 根据 field 设置 obj 中对应字段的值为 value
     * @param obj 需要操作的对象
     * @param field 字段名
     * @param value 值
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public void invokeSetMethod(Object obj, Field field, Object value) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        String methodName = "set" + captureName(field.getName());
        if (field.getType() == boolean.class && field.getName().startsWith("is")) {
            methodName = "set" + field.getName().substring(2);
        }
        Method[] methods = obj.getClass().getMethods();
        for (Method method: methods) {
            if (methodName.equals(method.getName())) {
                method.invoke(obj, value);
                return;
            }
        }
        throw new NoSuchMethodException("没有找到方法 " + methodName );
    }

    /**
     * 获取 obj 中 name 字段的值
     * @param obj 需要操作的对象
     * @param field 字段
     * @return
     * @throws NoSuchMethodException
     * @throws NoSuchFieldException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public Object invokeGetMethod(Object obj, Field field) throws NoSuchMethodException, NoSuchFieldException, InvocationTargetException, IllegalAccessException {

        Class clazz = obj.getClass();

        String baseName = captureName(field.getName());
        String[] methodName = new String[2];
        methodName[0] = "get" + baseName;
        if (field.getType() == boolean.class) {
            if (field.getName().startsWith("is")) {
                methodName[1] = field.getName();
            } else {
                methodName[1] = "is" + baseName;
            }
        }

        Method[] methods = clazz.getMethods();
        for (Method method: methods) {
            if (methodName[0].equals(method.getName()))
                return method.invoke(obj);
            if (methodName[1] != null && methodName[1].equals(method.getName()))
                return method.invoke(obj);
        }
        throw new NoSuchMethodException("没有找到方法 " + methodName[0] + (methodName[1] != null ? " 或 " + methodName[1] : ""));
    }

    private Field searchField(List<Field> fields, String name) {
        for (Field field: fields) {
            if (field.getName().equals(name))
                return field;
        }

        return null;
    }

    public Method getMethod(Class clazz, String methodName) throws NoSuchMethodException {
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method: methods) {
            if (method.getName().equals(methodName))
                return method;
        }

        Class superclass = clazz.getSuperclass();
        if (superclass == null)
            throw new NoSuchMethodException("没有找到方法 " + methodName);

        return getMethod(superclass, methodName);
    }

    private class ToClass {
        private String targetName;
        private String sourceName;
        private String format;
        private boolean translate = true;
        private Class<? extends IConvertHandler> executor = BasicConvertHandler.class;

        public String getTargetName() {
            return targetName;
        }

        public void setTargetName(String targetName) {
            this.targetName = targetName;
        }

        public String getSourceName() {
            return sourceName;
        }

        public void setSourceName(String sourceName) {
            this.sourceName = sourceName;
        }

        public String getFormat() {
            return format;
        }

        public void setFormat(String format) {
            this.format = format;
        }

        public boolean isTranslate() {
            return translate;
        }

        public void setTranslate(boolean translate) {
            this.translate = translate;
        }

        public Class<? extends IConvertHandler> getExecutor() {
            return executor;
        }

        public void setExecutor(Class<? extends IConvertHandler> executor) {
            this.executor = executor;
        }
    }

    /**
     * 将 obj 的值赋值给 t
     * @param target 接收值的对象
     * @param source 取值的对象
     * @param ignore 是否忽略空值
     * @param force 如果有值存在 是否强制覆盖
     * @param isTarget 是目标维护关系
     * @param <T>
     * @return
     */
    public <T> T assign(T target, Object source, boolean ignore, boolean force, boolean isTarget) throws Exception {

        List<Field> targetFields = getFields(target.getClass());
        List<Field> sourceFields = getFields(source.getClass());

        // 使用维护关系的 类属性进行循环
        List<Field> loopObj = isTarget?targetFields:sourceFields;

        for (Field field: loopObj) {
            int modifier = field.getModifiers();
            if (Modifier.isStatic(modifier) || Modifier.isFinal(modifier))
                continue;

            ToClass to = new ToClass();

            if (field.isAnnotationPresent(Handler.class)) {
                Handler toAnn = field.getAnnotation(Handler.class);
                String name = toAnn.name() == null || "".equals(toAnn.name()) ? field.getName() : toAnn.name();
                if (isTarget) {
                    // 目标维护属性
                    to.setTargetName(field.getName());
                    to.setSourceName(name);
                } else {
                    // 源维护属性
                    to.setTargetName(name);
                    to.setSourceName(field.getName());
                }
                to.setFormat(toAnn.format());
                to.setTranslate(toAnn.translate());
                to.setExecutor(toAnn.executor());
            } else {
                to.setTargetName(field.getName());
                to.setSourceName(to.getTargetName());
            }

            Field sourceField = searchField(sourceFields, to.getSourceName()); // 获取源字段属性
            if (sourceField == null) continue;
            Object sourceValue = invokeGetMethod(source, sourceField); // 获取值

            if (sourceValue == null && ignore) continue; // 忽略空

//            ITranslators translators = to.getExecutor().newInstance();
            IConvertHandler translators = SpringContextUtils.getInstance(to.getExecutor());

            Field targetField = searchField(targetFields, to.getTargetName()); // 获取源字段属性
            if (targetField == null) continue;

            if (invokeGetMethod(target, targetField) != null && !force) continue; // 已经有值且不进行强制覆盖，跳过

            Object targetValue = translators.handler(sourceValue, to.getFormat(), targetField.getType());

            invokeSetMethod(target, targetField, targetValue);
        }

        return target;
    }

    /**
     * 将 obj 的值赋值给 t
     * @param t 接收值的对象
     * @param obj 取值的对象
     * @param ignore 是否忽略空值
     * @param force 如果有值存在 是否强制覆盖
     * @param <T>
     * @return
     */
    public <T> T assign(T t, Object obj, boolean ignore, boolean force) throws Exception {
        return assign(t, obj, ignore, force, true);
    }

    /**
     * 将 obj 的值赋值给 t
     * @param t 接收值的对象
     * @param obj 取值的对象
     * @param ignore 是否忽略空值
     * @param <T>
     * @return
     */
    public <T> T assign(T t, Object obj, boolean ignore) throws Exception {
        return assign(t, obj, ignore, false);
    }

    /**
     * 将 obj 的值赋值给 t
     * @param t 接收值的对象
     * @param obj 取值的对象
     * @param <T>
     * @return
     */
    public <T> T assign(T t, Object obj) throws Exception {
        return assign(t, obj, true, false);
    }
}
